import string
from utils.single import SingletonMeta
import grpc
from services.protos import address_pb2, address_pb2_grpc
from .decorators import grpc_error_handler, grpc_retry_handler
from google.protobuf.empty_pb2 import Empty
from utils.tll_consul import ConsulClient
from loguru import logger
from typing import Optional
import asyncio

consul=ConsulClient()

class AddressStub:
    def __init__(self, max_connection_attempts: int = 3):
        self.max_connection_attempts = max_connection_attempts
        self.channel: Optional[grpc.aio.Channel] = None
        self.stub: Optional[address_pb2_grpc.AddressStub] = None
        
    @property
    async def user_service_addr(self) -> str:
        """获取用户服务地址，支持重试机制"""
        for attempt in range(self.max_connection_attempts):
            try:
                address, port = consul.get_one_user_service_address()
                
                if address is None:
                    logger.warning(f"地址服务第 {attempt + 1} 次尝试获取服务地址失败，尝试刷新服务发现...")
                    
                    # 刷新服务发现
                    await consul.fetch_user_service_addresses(only_healthy=True)
                    
                    if attempt < self.max_connection_attempts - 1:
                        await asyncio.sleep(1.0 * (attempt + 1))
                        continue
                    else:
                        raise RuntimeError("无法获取地址服务地址，请检查consul连接和服务注册")
                else:
                    service_addr = f"{address}:{port}"
                    logger.info(f"地址服务地址: {service_addr}")
                    return service_addr
                    
            except Exception as e:
                logger.error(f"获取地址服务地址异常 (尝试 {attempt + 1}/{self.max_connection_attempts}): {e}")
                if attempt < self.max_connection_attempts - 1:
                    await asyncio.sleep(1.0 * (attempt + 1))
                    continue
                else:
                    raise
        
        raise RuntimeError("所有尝试都失败，无法获取地址服务地址")
        
    async def __aenter__(self):
        service_addr = await self.user_service_addr
        self.channel = grpc.aio.insecure_channel(service_addr)
        self.stub = address_pb2_grpc.AddressStub(self.channel)
        return self.stub
    
    async def __aexit__(self, exc_type, exc_val, exc_tb):
        if self.channel:
            await self.channel.close()
            self.channel = None
            self.stub = None
        
class AddressServiceClient(metaclass=SingletonMeta): 
    @grpc_retry_handler(max_retries=2, retry_delay=1.0)
    async def CreateAddress(self,
                            user_id:int,
                            realname:str,
                            mobile:str,
                            region:str,
                            detail:str
                            ):
        async with AddressStub() as stub:
            request=address_pb2.CreateAddressRequest(user_id=user_id,realname=realname,mobile=mobile,region=region,detail=detail)# type: ignore
            response=await stub.CreateAddress(request)
            return response.address 
        
    @grpc_retry_handler(max_retries=2, retry_delay=0.5)
    async def UpdateAddress(self,address_id:str,realname:str,mobile:str,region:str,detail:str,user_id:int):
        async with AddressStub() as stub:
            request=address_pb2.UpdateAddressRequest(id=address_id,realname=realname,mobile=mobile,region=region,detail=detail,user_id=user_id)# type: ignore
            await stub.UpdateAddress(request)
            return Empty()
            
    @grpc_retry_handler(max_retries=2, retry_delay=0.5)
    async def DeleteAddress(self,id:str,user_id:int):
        async with AddressStub() as stub:
            request=address_pb2.DeleteAddressRequest(id=id,user_id=user_id)# type: ignore
            await stub.DeleteAddress(request)
            return Empty()
        
    @grpc_retry_handler(max_retries=2, retry_delay=1.0)
    async def GetAddressById(self,id:str,user_id:int):
        async with AddressStub() as stub:
            request=address_pb2.AddressIdRequest(id=id,user_id=user_id)# type: ignore
            response=await stub.GetAddressById(request)
            return response.address
        
    @grpc_retry_handler(max_retries=2, retry_delay=1.0)
    async def GetAddressList(self,user_id:int,page:int=1,size:int=10):
        async with AddressStub() as stub:
            request=address_pb2.AddressListRequest(user_id=user_id,page=page,size=size)# type: ignore
            response=await stub.GetAddressList(request)
            return response
        
   